/*****************************************************************************
 *
 * This MobilityDB code is provided under The PostgreSQL License.
 * Copyright (c) 2016-2024, Université libre de Bruxelles and MobilityDB
 * contributors
 *
 * MobilityDB includes portions of PostGIS version 3 source code released
 * under the GNU General Public License (GPLv2 or later).
 * Copyright (c) 2001-2024, PostGIS contributors
 *
 * Permission to use, copy, modify, and distribute this software and its
 * documentation for any purpose, without fee, and without a written
 * agreement is hereby granted, provided that the above copyright notice and
 * this paragraph and the following two paragraphs appear in all copies.
 *
 * IN NO EVENT SHALL UNIVERSITE LIBRE DE BRUXELLES BE LIABLE TO ANY PARTY FOR
 * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, INCLUDING
 * LOST PROFITS, ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION,
 * EVEN IF UNIVERSITE LIBRE DE BRUXELLES HAS BEEN ADVISED OF THE POSSIBILITY
 * OF SUCH DAMAGE.
 *
 * UNIVERSITE LIBRE DE BRUXELLES SPECIFICALLY DISCLAIMS ANY WARRANTIES,
 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
 * AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS ON
 * AN "AS IS" BASIS, AND UNIVERSITE LIBRE DE BRUXELLES HAS NO OBLIGATIONS TO
 * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
 *
 *****************************************************************************/

/**
 * @brief A simple program that reads from a CSV file synthetic trip data in
 * Brussels generated by the MobilityDB-BerlinMOD generator,
 * https://github.com/MobilityDB/MobilityDB-BerlinMOD
 * assembles the trips from the individual observations, and write them in a
 * CSV file named "berlinmod_trips_new.csv" which corresponds to the file
 * "berlinmod_trips.csv" in the data directory.
 *
 * The input file is
 * - `berlinmod_instants.csv`: individual observations from 55 trips of 5 cars
 *   during 4 days obtained from the generator at scale factor 0.005.
 *   This file is generated by the program `05_berlinmod_disassemble`.
 * In the above file, the coordinates are given in the 3857 coordinate system,
 * https://epsg.io/3857
 * and the timestamps are given in the Europe/Brussels time zone.
 * The input file can be generated by running the program
 * `05_berlinmod_disassemble.c` in the current directory
 * This simple program does not cope with erroneous inputs, such as missing
 * fields or invalid values.
 *
 * The program can be build as follows
 * @code
 * gcc -Wall -g -I/usr/local/include -o 03_berlinmod_assemble 03_berlinmod_assemble.c -L/usr/local/lib -lmeos
 * @endcode
 */

#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <meos.h>

/* Maximum number of instants in an input trip */
#define MAX_INSTANTS 50000
/* Number of instants in a batch for printing a marker */
#define NO_INSTANTS_BATCH 1000
/* Maximum length in characters of a header record in the input CSV file */
#define MAX_LENGTH_HEADER 1024
/* Maximum length in characters of a point in the input data */
#define MAX_LENGTH_POINT 100
/* Maximum length in characters of a date in the input data */
#define MAX_LENGTH_DATE 12
/* Maximum length in characters of a timestamp in the input data */
#define MAX_LENGTH_TIMESTAMP 32
/* Maximum number of trips */
#define MAX_TRIPS 5

typedef struct
{
  int tripid;
  int vehid;
  DateADT day;
  int seq;
  GSERIALIZED *point;
  TimestampTz t;
} trip_input_record;

typedef struct
{
  int tripid;      /* Identifier of the trip */
  int vehid;       /* Identifier of the vehicle */
  DateADT day;     /* Day of the trip */
  int seq;         /* Number of the trip in the day of the trip */
  int no_instants; /* Number of input instants */
  TInstant *trip_instants[MAX_INSTANTS]; /* Array of instants for the trip */
  Temporal *trip;  /* Trip constructed from the input instants */
} trip_output_record;


/* Main program */
int main(void)
{
  /* Get start time */
  clock_t t;
  t = clock();

  /* Initialize MEOS */
  meos_initialize();

  /* Allocate space to build the trips */
  trip_output_record trips[MAX_TRIPS] = {0};
  /* Number of trips */
  int no_trips = 0;
  /* Iterator variables */
  int i, j;
  /* Return value */
  int return_value = 0;

  /* Substitute the full file path in the first argument of fopen */
  FILE *file = fopen("data/berlinmod_instants.csv", "r");

  if (! file)
  {
    printf("Error opening input file\n");
    meos_finalize();
    return 1;
  }

  trip_input_record rec;
  int no_records = 0;
  int no_nulls = 0;
  char header_buffer[MAX_LENGTH_HEADER];
  char point_buffer[MAX_LENGTH_POINT];
  char date_buffer[MAX_LENGTH_DATE];
  char timestamp_buffer[MAX_LENGTH_TIMESTAMP];

  /* Read the first line of the file with the headers */
  fscanf(file, "%1023s\n", header_buffer);

  printf("Reading the instants (one '*' marker every %d instants)\n",
    NO_INSTANTS_BATCH);

  /* Continue reading the file */
  do
  {
    int read = fscanf(file, "%d,%d,%10[^,],%d,%99[^,],%31[^\n]\n",
      &rec.tripid, &rec.vehid, date_buffer, &rec.seq, point_buffer,
      timestamp_buffer);
    /* Transform the string representing the date into a date value */
    rec.day = date_in(date_buffer);
    /* Transform the string representing the trip into a temporal value */
    rec.point = geom_in(point_buffer, -1);
    /* Transform the string representing the timestamp into a timestamp value */
    rec.t = timestamp_in(timestamp_buffer, -1);
    /* Transform the string representing the trip into a temporal value */
    TInstant *inst = tpointinst_make(rec.point, rec.t);
    /* Free the point as it's not needed anymore */
    free(rec.point);

    if (read == 6)
    {
      no_records++;
      if (no_records % NO_INSTANTS_BATCH == 0)
      {
        printf("*");
        fflush(stdout);
      }
    }

    if (read != 6 && ! feof(file))
    {
      printf("Record with missing values ignored\n");
      no_nulls++;
    }

    if (ferror(file))
    {
      printf("Error reading input file");
      return_value = 1;
      goto cleanup;
    }

    /* Find the place to store the new instant */
    int trip = -1;
    for (i = 0; i < MAX_TRIPS; i++)
    {
      if (trips[i].tripid == rec.tripid)
      {
        trip = i;
        break;
      }
    }
    if (trip < 0)
    {
      trip = no_trips++;
      if (trip == MAX_TRIPS)
      {
        printf("The maximum number of trips in the input file is bigger than %d",
          MAX_TRIPS);
        return_value = 1;
        goto cleanup;
      }
    }

    /*
     * Store the input values
     */
    trips[trip].tripid = rec.tripid;
    trips[trip].vehid = rec.vehid;
    trips[trip].day = rec.day;
    trips[trip].seq = rec.seq;
    trips[trip].trip_instants[trips[trip].no_instants++] = inst;
  } while (!feof(file));

  /* Close the input file */
  fclose(file);

  printf("\n%d records read from file 'berlinmod_instants.csv'.\n"
    "%d incomplete records ignored.\n", no_records, no_nulls);
  printf("%d trips read.\n", no_trips);

  /* Construct the trips */
  for (i = 0; i < no_trips; i++)
  {
    trips[i].trip = (Temporal *) tsequence_make(
      (const TInstant **) trips[i].trip_instants,
      trips[i].no_instants, true, true, LINEAR, true);
    printf("TripId: %d, Number of input instants: %d, Distance travelled %lf\n",
      trips[i].tripid, trips[i].no_instants, tpoint_length(trips[i].trip));
  }

  /* Open the output file */
  file = fopen("data/berlinmod_trips_new.csv", "w+");
  printf("Assembled trips written to file 'berlinmod_trips_new.csv'\n");

  /* Write the header line */
  fprintf(file,"tripid,vehid,day,seqno,trip\n");

  /* Loop for each trip */
  for (i = 0; i < no_trips; i++)
  {
    /* Write line in the CSV file */
    char *date_str = date_out(trips[i].day);
    size_t length;
    /* Encode using server machine endian */
    char *trip_str = temporal_as_hexwkb(trips[i].trip, WKB_EXTENDED, &length);
    fprintf(file,"%d,%d,%s,%d,%s\n", trips[i].tripid, trips[i].vehid, date_str,
      trips[i].seq, trip_str);
    free(date_str); free(trip_str);
  }

  /* Close the file */
  fclose(file);

  /* Calculate the elapsed time */
  t = clock() - t;
  double time_taken = ((double) t) / CLOCKS_PER_SEC;
  printf("The program took %f seconds to execute\n", time_taken);

/* Clean up */
cleanup:

 /* Free memory */
  for (i = 0; i < no_trips; i++)
  {
    free(trips[i].trip);
    for (j = 0; j < trips[i].no_instants; j++)
      free(trips[i].trip_instants[j]);
  }

  /* Finalize MEOS */
  meos_finalize();

  return return_value;
}
